﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using RapidSpider.DownloadAgents;
using System.IO;
using System.Diagnostics;
using RapidSpider.Enumerators;
using System.Runtime.InteropServices;

namespace RapidSpider.Views
{
    public partial class MainView : Form
    {
        #region Constants
        private const int SNAP_OFFSET = 30;
        private const int WM_WINDOWPOSCHANGING = 70;
        private const int WM_DRAWCLIPBOARD = 776;
        public const int COLUMN_INDEX_FILENAME = 0;
        public const int COLUMN_INDEX_SIZE = 1;
        public const int COLUMN_INDEX_SERVER = 2;
        public const int COLUMN_INDEX_PROGRESS = 3;
        public const int COLUMN_INDEX_SPEED = 4;
        public const int COLUMN_INDEX_STATUS = 5;
        #endregion

        #region Structs
        [StructLayout(LayoutKind.Sequential)]
        public struct WINDOWPOS
        {
            public IntPtr hwnd;
            public IntPtr hwndInsertAfter;
            public int x;
            public int y;
            public int cx;
            public int cy;
            public int flags;
        }
        #endregion

        #region Members
        private MainViewStatus _Status = MainViewStatus.Stopped;
        #endregion

        #region Properties
        public MainViewStatus Status
        {
            get { return this._Status; }
            set 
            { 
                this._Status = value;
                this.SetControlButtons();
                if (null != this.StatusChanged)
                {
                    this.StatusChanged(this, new EventArgs());
                }
            }
        }

        public ToolStripComboBox comboboxURI
        {
            get { return this._comboboxURI; }
        }

        public ToolStripButton buttonOpenFileDialog
        {
            get { return this._buttonOpenFileDialog; }
        }

        public ToolStripButton buttonAddDownload
        {
            get { return this._buttonAddDownload; }
        }

        public ToolStripButton buttonReinitialise
        {
            get { return this._buttonReinitialise; }
        }

        public ToolStripButton buttonNavigateURI
        {
            get { return this._buttonNavigateURI; }
        }

        public ToolStripButton buttonClearHistory
        {
            get { return this._buttonClearHistory; }
        }

        public ToolStripButton buttonPlayAll
        {
            get { return this._buttonPlayAll; }
        }

        public ToolStripButton buttonPauseAll
        {
            get { return this._buttonPauseAll; }
        }

        public ToolStripButton buttonStopAll
        {
            get { return this._buttonStopAll; }
        }

        public ToolStripButton buttonSettings
        {
            get { return this._buttonSettings; }
        }

        public ToolStripButton buttonOpenTargetFolder
        {
            get { return this._buttonOpenTargetFolder; }
        }

        public ToolStripButton buttonShowLogs
        {
            get { return this._buttonShowLogs; }
        }

        public ToolStripButton buttonCheckForUpdates
        {
            get { return this._buttonCheckForUpdates; }
        }

        public DataGridView gridDownloads
        {
            get { return this._gridDownloads; }
        }

        public ToolStripMenuItem menuitemPlaySelected
        {
            get { return this._menuitemPlaySelected; }
        }

        public ToolStripMenuItem menuitemPauseSelected
        {
            get { return this._menuitemPauseSelected; }
        }

        public ToolStripMenuItem menuitemResumeSelected
        {
            get { return this._menuitemResumeSelected; }
        }

        public ToolStripMenuItem menuitemMoveUp
        {
            get { return this._menuitemMoveUp; }
        }

        public ToolStripMenuItem menuitemMoveDown
        {
            get { return this._menuitemMoveDown; }
        }

        public ToolStripMenuItem menuitemDeleteSelected
        {
            get { return this._menuitemDeleteSelected; }
        }

        public OpenFileDialog OpenFileDialog
        {
            get { return this._OpenFileDialog; }
        }

        public DataGridViewRow SelectedRow
        {
            get
            {
                for (int i = 0; i < this._gridDownloads.Rows.Count; i++)
                {
                    if (this._gridDownloads.Rows[i].Selected)
                    {
                        return this._gridDownloads.Rows[i];
                    }
                }
                return null;
            }
        }

        public NotifyIcon NotifyIcon
        {
            get { return this._NotifyIcon; }
        }

        public ToolStripMenuItem menuitemCheckForUpdates
        {
            get { return this._menuitemCheckForUpdates; }
        }

        public ToolStripMenuItem menuitemPlayAll
        {
            get { return this._menuitemPlayAll; }
        }

        public ToolStripMenuItem menuitemPauseAll
        {
            get { return this._menuitemPauseAll; }
        }

        public ToolStripMenuItem menuitemStopAll
        {
            get { return this._menuitemStopAll; }
        }

        public ToolStripMenuItem menuitemExit
        {
            get { return this._menuitemExit; }
        }

        public ToolStripLabel labelVersion
        {
            get { return this._labelVersion; }
        }
        #endregion

        #region Methods
        public MainView()
        {
            InitializeComponent();

            this._menuitemAuthor.Text = String.Format(
                CultureInfo.CurrentCulture,
                "www.ne0ge0.com // {0}",
                DateTime.Now.Year);

            SetClipboardViewer(this.Handle.ToInt32());
        }

        #region Declarations
        [DllImport("user32.dll")]
        public static extern int SetClipboardViewer(int windowHandle);
        #endregion

        #region Event Handlers
        // Future handling of clipboard monitor
        protected override void WndProc(
            ref Message theMessage)
        {
            base.WndProc(ref theMessage);

            switch (theMessage.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    break;
                case WM_WINDOWPOSCHANGING:
                    SnapToDesktopBorder(this, theMessage.LParam, 0);
                    break;
            } 
        }
        
        private void _menuitemAuthor_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            Process.Start("http://www.ne0ge0.com");
        }

        private void _textboxURI_Enter(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (this._comboboxURI.Text.Equals("<Type or Paste URI or Path here>"))
            {
                this._comboboxURI.Text = String.Empty;
            }
        }

        private void _textboxURI_Leave(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (String.IsNullOrEmpty(this._comboboxURI.Text))
            {
                this._comboboxURI.Text = "<Type or Paste URI or Path here>";
            }
        }

        private void _gridDownloads_ColumnWidthChanged(
            object sender, 
            DataGridViewColumnEventArgs e)
        {
            this.Refresh();
        }
        #endregion

        #region Private Helpers
        public void RepopulateURIComboBox()
        {
            RegistryAgent.LoadDownloadHistory();

            this._comboboxURI.Items.Clear();

            Array theDownloadHistory = RegistryAgent.DownloadHistory.ToArray();

            for (int i = 0; i < theDownloadHistory.Length; i++)
            {
                this._comboboxURI.Items.Add(theDownloadHistory.GetValue(i));
            }

            this._buttonClearHistory.Enabled =
                !this._comboboxURI.Items.Count.Equals(0);
        }

        public override void Refresh()
        {
            this._comboboxURI.Width =
                this._ToolStrip.DisplayRectangle.Width - 296;

            base.Refresh();
        }

        public int AddRow(
            FileSystemInfo theTargetFile,
            string theStatusMessage)
        {
            return this._gridDownloads.Rows.Add(
                theTargetFile.Name, // Filename
                String.Empty,       // Speed
                String.Empty,       // Server
                "0%",               // Progress
                String.Empty,       // Speed
                theStatusMessage);  // Status
        }

        public void RemoveRow(
            IDownloadAgent theInstance)
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.InvokeRequired)
            {
                this.Invoke(
                    new RemoveRowHandler(this.RemoveRow), 
                    theInstance);
            }
            else
            {
                for (int i = 0; i < this._gridDownloads.Rows.Count; i++)
                {
                    if (this._gridDownloads.Rows[i].Tag.Equals(theInstance))
                    {
                        this._gridDownloads.Rows.RemoveAt(i);
                        break;
                    }
                }

                this._gridDownloads.ClearSelection();

                Application.DoEvents();
            }
        }
        
        public void SetControlButtons()
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.InvokeRequired)
            {
                this.Invoke(
                    new SetControlButtonsHandler(SetControlButtons));
            }
            else
            {
                switch (this.Status)
                {
                    case MainViewStatus.Paused:
                        this._buttonPlayAll.Enabled = 
                            this._menuitemPlayAll.Enabled =
                                true;
                        this._buttonPauseAll.Enabled = 
                            this._menuitemPauseAll.Enabled =
                                false;
                        this._buttonStopAll.Enabled = 
                            this._menuitemStopAll.Enabled =
                                true;
                        break;
                    case MainViewStatus.Started:
                        this._buttonPlayAll.Enabled =
                            this._menuitemPlayAll.Enabled =
                                false;
                        this._buttonPauseAll.Enabled =
                            this._menuitemPauseAll.Enabled =
                                true;
                        this._buttonStopAll.Enabled =
                            this._menuitemStopAll.Enabled =
                                true;
                        break;
                    default:
                        this._buttonPlayAll.Enabled =
                            this._menuitemPlayAll.Enabled =
                                true;
                        this._buttonPauseAll.Enabled =
                            this._menuitemPauseAll.Enabled =
                                false;
                        this._buttonStopAll.Enabled =
                            this._menuitemStopAll.Enabled =
                                false;
                        break;
                }
            }
        }
        #endregion

        #region Snap To Border Functionality
        public void SnapToDesktopBorder(
            Form theClientForm,
            IntPtr LParam,
            int theWidthAdjustment)
        {
            if (theClientForm == null)
            {
                throw new ArgumentNullException();
            }

            // Snap client to the top, left, bottom or right desktop border
            // as the form is moved near that border.
            try
            {
                // Marshal the LPARAM value which is a WINDOWPOS struct
                WINDOWPOS theNewPosition = new WINDOWPOS();

                theNewPosition = (WINDOWPOS)Marshal.PtrToStructure(
                    LParam, theNewPosition.GetType());

                if (theNewPosition.y.Equals(0) || theNewPosition.x.Equals(0))
                {
                    return;
                }

                // Adjust the client size for borders and caption bar
                Rectangle theClientRectangle =
                    theClientForm.RectangleToScreen(theClientForm.ClientRectangle);

                theClientRectangle.Width +=
                    SystemInformation.FrameBorderSize.Width - theWidthAdjustment;
                theClientRectangle.Height +=
                    SystemInformation.FrameBorderSize.Height + SystemInformation.CaptionHeight;

                // Now get the screen working area (without taskbar)
                Rectangle theWorkingRectangle =
                    Screen.FromControl(theClientForm).WorkingArea;

                // Left border
                if (theNewPosition.x >= theWorkingRectangle.X - SNAP_OFFSET &&
                    theNewPosition.x <= theWorkingRectangle.X + SNAP_OFFSET)
                {
                    theNewPosition.x = theWorkingRectangle.X;
                }

                // Get screen bounds and taskbar height (when taskbar is horizontal)
                Rectangle theScreenRectangle =
                    Screen.FromControl(theClientForm).Bounds;
                int theTaskBarHeight =
                    theScreenRectangle.Height = theWorkingRectangle.Height;

                // Top border (check if taskbar is on top or bottom via WorkingRect.Y)
                if (theNewPosition.y >= -SNAP_OFFSET &&
                    (theWorkingRectangle.Y > 0 && theNewPosition.y <= (theTaskBarHeight + SNAP_OFFSET)) ||
                    (theWorkingRectangle.Y <= 0 && theNewPosition.y <= SNAP_OFFSET))
                {
                    if (theTaskBarHeight > 0)
                    {
                        theNewPosition.y = theWorkingRectangle.Y; // Horizontal Taskbar
                    }
                    else
                    {
                        theNewPosition.y = 0; // Vertical Taskbar
                    }
                }

                // Right border
                if (theNewPosition.x + theClientRectangle.Width <= theWorkingRectangle.Right + SNAP_OFFSET &&
                    theNewPosition.x + theClientRectangle.Width >= theWorkingRectangle.Right - SNAP_OFFSET)
                {
                    theNewPosition.x =
                        theWorkingRectangle.Right - (theClientRectangle.Width + SystemInformation.FrameBorderSize.Width);
                }

                // Bottom border
                if (theNewPosition.y + theClientRectangle.Height <= theWorkingRectangle.Bottom + SNAP_OFFSET &&
                    theNewPosition.y + theClientRectangle.Height >= theWorkingRectangle.Bottom - SNAP_OFFSET)
                {
                    theNewPosition.y =
                        theWorkingRectangle.Bottom - (theClientRectangle.Height + SystemInformation.FrameBorderSize.Height);
                }

                // Marshal it back
                Marshal.StructureToPtr(theNewPosition, LParam, true);
            }
            catch
            {
                throw;
            }
        }
        #endregion
        #endregion

        #region Delegates
        private delegate void SetControlButtonsHandler();
        private delegate void RemoveRowHandler(IDownloadAgent theInstance);
        #endregion

        #region Events
        public event EventHandler StatusChanged;
        #endregion
    }
}
